﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace Server.TCP
{
    public class Decoder
    {
        public const byte Head = 0x7E;
        public const byte HeadEscape = 0x7D;
        public const byte HeadEscape1 = 0x02;
        public const byte HeadEscape2 = 0x01;

        public const byte Tail = 0x7C;
        public const byte TailEscape = 0x7B;
        public const byte TailEscape1 = 0x02;
        public const byte TailEscape2 = 0x01;

        private StateObject _state;
        private Stream _stream;

        public int CommandID { get; set; }
        public int SerialNumber { get; set; }
        public byte[] CommandParameter { get; set; }

        public Decoder(StateObject state)
        {
            _state = state;
        }

        public bool IsEOF()
        {
            if (_state.Datas.Count == 0)
                return false;

            byte[] data = _state.Datas.Dequeue();
            for (int i = 0; i < data.Length; i++)
            {
                byte value = data[i];
                switch (value)
                {
                    case Head:
                        _stream = new MemoryStream();
                        break;
                    case Tail:
                        return true;
                    default:
                        if (_stream != null)
                            _stream.WriteByte(data[i]);
                        break;
                }
            }
            return false;
        }

        public void Decode()
        {
            _stream.Position = 0;
            using (_stream)
            {
                byte header0 = this.Read(_stream);
                byte header1 = this.Read(_stream);
                CommandID = (ushort)((header0 << 8) + header1);

                byte header2 = this.Read(_stream);
                byte header3 = this.Read(_stream);
                byte header4 = this.Read(_stream);
                byte header5 = this.Read(_stream);
                SerialNumber = (header2 << 24) + (header3 << 16) + (header4 << 8) + (header5 << 0);

                List<byte> list = new List<byte>();
                while (_stream.Position != _stream.Length)
                {
                    list.Add(this.Read(_stream));
                }

                CommandParameter = list.ToArray();
            }
        }

        private byte Read(Stream stream)
        {
            byte value = (byte)stream.ReadByte();
            switch (value)
            {
                case HeadEscape:
                    switch ((byte)stream.ReadByte())
                    {
                        case HeadEscape1:
                            return Head;
                        case HeadEscape2:
                            return HeadEscape;
                        default:
                            throw new FormatException();
                    }
                case TailEscape:
                    switch ((byte)stream.ReadByte())
                    {
                        case TailEscape1:
                            return Tail;
                        case TailEscape2:
                            return TailEscape;
                        default:
                            throw new FormatException();
                    }
            }

            return value;
        }
    }


    public class Encoder
    {
        public byte[] Encode(int commandID, int serialNumber, byte[] bytes)
        {
            byte[] header = new byte[6];
            header[0] = (byte)(commandID >> 8);
            header[1] = (byte)(commandID >> 0);
            header[2] = (byte)(serialNumber >> 24);
            header[3] = (byte)(serialNumber >> 16);
            header[4] = (byte)(serialNumber >> 8);
            header[5] = (byte)(serialNumber >> 0);

            MemoryStream stream = new MemoryStream();
            stream.WriteByte(Decoder.Head);
            for (int i = 0; i < header.Length; i++)
            {
                this.Write(stream, header[i]);
            }

            for (int i = 0; i < bytes.Length; i++)
            {
                this.Write(stream, bytes[i]);
            }

            stream.WriteByte(Decoder.Tail);
            return stream.ToArray();
        }

        private void Write(MemoryStream stream, byte value)
        {
            switch (value)
            {
                case Decoder.Head:
                    stream.WriteByte(Decoder.HeadEscape);
                    stream.WriteByte(Decoder.HeadEscape1);
                    break;
                case Decoder.HeadEscape:
                    stream.WriteByte(Decoder.HeadEscape);
                    stream.WriteByte(Decoder.HeadEscape2);
                    break;
                case Decoder.Tail:
                    stream.WriteByte(Decoder.TailEscape);
                    stream.WriteByte(Decoder.TailEscape1);
                    break;
                case Decoder.TailEscape:
                    stream.WriteByte(Decoder.TailEscape);
                    stream.WriteByte(Decoder.TailEscape2);
                    break;
                default:
                    stream.WriteByte(value);
                    break;
            }
        }
    }
}
